# -*- coding: utf-8 -*-
# Copyright 2011-2012 MagiCycles.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from google.appengine.api import taskqueue

from common import UserExistError,MEMBER_TYPE,OutSitesTypes,LANGUAGE,PAGE_SIZE
from common import ACTIVITY_TYPE,ActivityType,VIP_POINT,ADV_POINT,CommentCategory

from gears import Bike
from models import DBBike,DBStatistics,DBActivity,DBComment,DBRoute,is_orderable
from models import DBUser,DBOutSiteUser,DBRide,DBTeam


class Activity(object):
    """ An Activity """
    DBTYPE = DBActivity
    Types = ACTIVITY_TYPE
    _activity_targets = {}

    def __init__(self, dbactivity=None):
        self.dbactivity = dbactivity

    def __getattr__(self, name):
        if name == "dbactivity":
            return None
        return getattr(self.dbactivity, name)

    @property
    def Action(self):
        return self.dbactivity.activity_type

    @property
    def Rider(self):
        return SiteUser(self.dbactivity.rider)

    @property
    def CreateDateTime(self):
        return self.dbactivity.create_datetime

    @property
    def Target(self):
        target_class = self.ACTIVITY_TARGETS.get(self.dbactivity.activity_type)
        try:
            return target_class(target_class.DBTYPE.get_by_id(long(self.object_id_or_name)))
        except Exception:
            return target_class(target_class.DBTYPE.get_by_key_name(self.object_id_or_name))

    @property
    def ACTIVITY_TARGETS(self):
        if not Activity._activity_targets:
            from gears import *
            from routes import *
            Activity._activity_targets = {
                ActivityType.CreateRoute: Route,
                ActivityType.AddBike: Bike,
                ActivityType.RemoveBike: Bike,
                ActivityType.AddRide: Ride,
                ActivityType.AddFriend: SiteUser,
                ActivityType.DeFriend: SiteUser,
                ActivityType.JoinTeam: Team,
                ActivityType.QuitTeam: Team,
                ActivityType.AddComment: Comment,
                ActivityType.AddFavorite: Route,
                ActivityType.RemoveFavorite: Route,
                ActivityType.AddTeam: Team,
                ActivityType.DismissTeam: Team,
                }
        return Activity._activity_targets

    @classmethod
    def add_route(cls, user, route):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, route.dbroute, ActivityType.CreateRoute)
        user.post_activity(cls(dbactivity))
        
    @classmethod
    def add_bike(cls, user, bike):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, bike.dbbike, ActivityType.AddBike)
        user.post_activity(cls(dbactivity))

    @classmethod
    def add_ride(cls, user, ride):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, ride.dbride, ActivityType.AddRide)
        user.post_activity(cls(dbactivity))

    @classmethod
    def add_friend(cls, user, friend):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, friend.dbuser, ActivityType.AddFriend)
        user.post_activity(cls(dbactivity))

    @classmethod
    def remove_friend(cls, user, friend):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, friend.dbuser, ActivityType.DeFriend)
        user.post_activity(cls(dbactivity))

    @classmethod
    def join_team(cls, user, team):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, team.dbteam, ActivityType.JoinTeam)
        user.post_activity(cls(dbactivity))

    @classmethod
    def add_comment(cls, user, comment):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, comment.dbcomment, ActivityType.AddComment)
        user.post_activity(cls(dbactivity))

    @classmethod
    def add_favorite(cls, user, route):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, route.dbroute, ActivityType.AddFavorite)
        user.post_activity(cls(dbactivity))

    @classmethod
    def remove_favorite(cls, user, route):
        dbactivity = DBActivity.create_rider_activity(user.dbuser, route.dbroute, ActivityType.RemoveFavorite)
        user.post_activity(cls(dbactivity))

    @classmethod
    def get_activities(cls, siteuser, pagesize=PAGE_SIZE, start_cursor=None):
        order = '-create_datetime'
        query = siteuser.dbuser.my_activities.order(order)
        query.with_cursor(start_cursor=start_cursor)
        activitieslist = [cls(dbactivity) for dbactivity in query.fetch(pagesize) \
                                if dbactivity is not None and not dbactivity.deleted]
        end_cursor = query.cursor()
        return activitieslist,end_cursor

    @classmethod
    def get_by_id(cls, id):
        dbactivity = DBActivity.get_by_id(id)
        return dbactivity and cls(dbactivity)


class OutSiteUser(object):
    """ A OutSite User """
    DBTYPE = DBOutSiteUser
    SITES = OutSitesTypes

    def __init__(self, dboutuser=None):
        self.dboutuser = dboutuser

    def __getattr__(self, name):
        if name == "dboutuser":
            return None
        return getattr(self.dboutuser, name)

    def __eq__(self,other):
        if not isinstance(other, OutSiteUser):
            return False
        return self.uid == other.uid

    def __ne__(self, other):
        if not isinstance(other, OutSiteUser):
            return True
        return self.uid != other.uid

    @property
    def Site_User(self):
        return SiteUser.get_by_uid(self.site_user and self.site_user.uid)

    @classmethod
    def check_exist(cls, uid):
        return DBOutSiteUser.check_dboutsiteuser_exist(uid)

    @classmethod
    def get_by_uid(cls, uid):
        dboutuser = DBOutSiteUser.check_dboutsiteuser_exist(uid)
        if not dboutuser:
            return None
        return cls(dboutuser)

    @classmethod
    def create(cls, uid, name="", **kwds):
        if DBOutSiteUser.check_dboutsiteuser_exist(uid):
            raise UserExistError("user %s is existed."%uid)
        name = name or uid
        dboutuser = DBOutSiteUser.create(uid, name, **kwds)
        outuser = cls(dboutuser)
        return outuser

    @classmethod
    def get_or_create(cls, uid, **kwds):
        dboutuser = DBOutSiteUser.get_or_create(uid, **kwds)
        return cls(dboutuser)

    def set_settings(self, **kwds):
        for key in kwds:
            setattr(self.dboutuser, key, kwds[key])

    def post_activity(self, activity):
        try:
            from settings import Site_Settings
        except:
            Site_Settings = {}

        if Site_Settings.get("enable_post_activity") and self.is_tweetable and self.enable_twitter:
            # send activity to taskqueue
            taskqueue.add(url = Site_Settings.get("post_activity_queue_url", "/queue/post_activity"),
                          params = {'uid': self.uid,
                                    'activity_id': activity.id})


class SiteUser(object):
    """ A User"""
    DBTYPE = DBUser
    Types = MEMBER_TYPE

    def __init__(self, dbuser=None):
        self.dbuser = dbuser
        self._outusers = None
        self._cursor_ride = None

    def __getattr__(self, name):
        if name == "dbuser":
            return None
        return getattr(self.dbuser, name)

    def __eq__(self,other):
        if not isinstance(other, SiteUser):
            return False
        return self.uid == other.uid

    def __ne__(self, other):
        if not isinstance(other, SiteUser):
            return True
        return self.uid != other.uid

    @property
    def Bikes(self):
        dbbikes = DBBike.get_by_id_or_name(self.dbuser.bikes)
        bikes = (Bike(dbbike=dbbike) for dbbike in dbbikes if dbbike is not None and not dbbike.deleted)
        return bikes

    @property
    def BikesCount(self):
        return len(self.dbuser.bikes)

    @property
    def Friends(self):
        dbfriends = DBUser.get_by_id_or_name(self.dbuser.friends)
        return (SiteUser(dbuser=dbuser) for dbuser in dbfriends if dbuser is not None and not dbuser.delted)

    @property
    def FriendsCount(self):
        return len(self.dbuser.friends)

    @property
    def FriendsInRequest(self):
        dbfriends = DBUser.get_by_id_or_name(self.dbuser.friendrequestlist)
        return (SiteUser(dbuser=dbuser) for dbuser in dbfriends if dbuser is not None)

    @property
    def FriendsCountInRequest(self):
        return len(self.dbuser.friendrequestlist)

    @property
    def LatestActivities(self):
        activities,end_cursor =  Activity.get_activities(self, PAGE_SIZE, None)
        return activities

    @property
    def LatestRides(self):
        from routes import Ride
        rides,self._cursor_ride = Ride.get_by_rider(self, pagesize=PAGE_SIZE, order="-start_datetime")
        return rides

    @property
    def Cursor_Ride(self):
        return self._cursor_ride

    @property
    def Stats(self):
        return DBStatistics.get_by_rider(self.dbuser)

    @property
    def Point(self):
        return self.Stats.point or 0

    @property
    def Stars(self):
        return xrange(0, long(self.Point%VIP_POINT/ADV_POINT))

    @property
    def Suns(self):
        return xrange(0, long(self.Point/VIP_POINT))

    @property
    def Favorites(self):
        from routes import Route
        return Route.get_by_ids(self.favorite_routes)

    @property
    def LatestRoutes(self):
        from routes import Route
        routeslist,end_cursor = \
            Route.get_routes_by_user(self, pagesize=PAGE_SIZE, start_cursor=None, order='-create_datetime')
        return routeslist

    @property
    def OutUsers(self):
        if not self._outusers:
            self._outusers = {}
            for outuser in DBOutSiteUser.get_by_id_or_name(self.dbuser.outsiteusers) or []:
                self._outusers[outuser.site] = OutSiteUser(outuser)
        return self._outusers

    @classmethod
    def create_from_outsiteuser(cls, outsiteuser, uid, name,  **kwds):
        uid = uid.lower()
        if DBUser.check_dbuser_exist(uid):
            raise UserExistError("user %s is existed."%uid)
        dbuser = DBUser.new_dbuser_with_outsiteuser(outsiteuser.dboutuser, uid, name, **kwds)
        DBStatistics.add_rider(dbuser)
        user = cls(dbuser=dbuser)
        return user

    @classmethod
    def get_or_create(cls, uid, name, **kwds):
        newuser = not DBUser.check_dbuser_exist(uid)
        dbuser = DBUser.get_or_create(uid, name, **kwds)
        user = cls(dbuser=dbuser)
        if newuser:
            DBStatistics.add_rider(dbuser)

        return user

    @classmethod
    def get_by_outsiteuser(cls, outsiteuser):
        dbuser = DBUser.get_by_outsiteuser(outsiteuser)
        return dbuser and cls(dbuser=dbuser)

    @classmethod
    def get_by_uid(cls, uid):
        dbuser = DBUser.check_dbuser_exist(uid)
        return dbuser and cls(dbuser=dbuser)

    @classmethod
    def check_user_exist(cls, uid):
        return DBUser.check_dbuser_exist(uid) is not None

    @classmethod
    def count_by_center(cls, center, max_distance= 20000):
        return DBUser.proximity_count(center, max_distance)

    @classmethod
    def search_by_center(cls, center, max_results=1000, max_distance= 20000, order="-last_login_datetime"):
        if not is_orderable(DBUser, order):
            order = '-last_login_datetime'
        #riders_query = DBUser.all().order(order)
        #results = DBUser.proximity_fetch(
            #riders_query,
            #center, max_results=max_results, max_distance=max_distance)
        results = DBUser.proximity_search(center, max_results=max_results, max_distance=max_distance, order=order)
        return [cls(dbuser) for dbuser in results if dbuser is not None and not dbuser.deleted]

    @classmethod
    def count_by_bounds(cls, bounds):
        return DBUser.bounding_box_count(bounds)

    @classmethod
    def search_by_bounds(cls, bounds, max_results=1000, order="-last_login_datetime"):
        if not is_orderable(DBUser, order):
            order = '-last_login_datetime'
        #riders_query = DBUser.all().order(order)
        #results = DBUser.bounding_box_fetch(
            #riders_query,
            #bounds, max_results=max_results)
        results = DBUser.bounding_box_search(bounds, max_results=max_results, order=order)
        return [cls(dbuser) for dbuser in results if dbuser is not None and not dbuser.deleted]

    @classmethod
    def new_friend_request(cls, me_uid, friend_uid):
        DBUser.new_friend_request(me_uid, friend_uid)  # new a request in rider's friend requests list

    @classmethod
    def confirm_friend_request(cls,  me_uid, friend_uid, agree=True):
        me, friend = DBUser.confirm_friend_request(me_uid, friend_uid, agree)
        if agree and me and friend:
            Activity.add_friend(cls(me), cls(friend))
            DBStatistics.add_friend(me, friend)

    @classmethod
    def remove_friendship(cls,  me_uid, friend_uid):
        me, friend = DBUser.remove_a_friendship(me_uid, friend_uid)
        if me and friend:
            Activity.remove_friend(cls(me), cls(friend))
            DBStatistics.remove_friend(me, friend)

    @classmethod
    def bind_outsite_user(cls, me, outsite_user):
        if not isinstance(me, SiteUser) or not isinstance(outsite_user, OutSiteUser):
            return False
        return DBUser.bind_outsite_user(me.dbuser, outsite_user.dboutuser)

    @classmethod
    def unbind_outsite_user(cls, me, outsite_user):
        if not isinstance(me, SiteUser) or not isinstance(outsite_user, OutSiteUser):
            return False
        return DBUser.unbind_outsite_user(me.dbuser, outsite_user.dboutuser)

    @classmethod
    def _get_riders_by_query(cls, query, pagesize=50, start_cursor=None):
        query.with_cursor(start_cursor=start_cursor)
        riderslist = [cls(dbuser) for dbuser in query.fetch(pagesize) if dbuser is not None and not dbuser.deleted]
        end_cursor = query.cursor()
        return riderslist,end_cursor

    @classmethod
    def search_by_text(cls, search_text, pagesize=50, start_cursor=None, order="-last_login_datetime"):
        if not is_orderable(cls.DBTYPE, order):
            order = '-last_login_datetime'
        query = cls.DBTYPE.all().order(order).search(search_text, properties=cls.DBTYPE.SearchableProperties()[0])
        return cls._get_riders_by_query(query, pagesize=pagesize, start_cursor=start_cursor)

    def set_settings(self, **kwds):
        for key,val in kwds.iteritems():
            setattr(self.dbuser, key, val)

    def add_favorite(self, routeid):
        from routes import Route
        route = Route.get_by_id(routeid)
        if route and routeid not in self.favorite_routes:
            self.favorite_routes.insert(0, route.id)
            self.save()
            DBStatistics.add_fav(self.dbuser, route.dbroute)
            Activity.add_favorite(self, route)
        return route

    def remove_favorite(self, routeid):
        from routes import Route
        route = Route.get_by_id(routeid)
        if routeid in self.favorite_routes:
            self.favorite_routes.remove(routeid)
            self.save()
        if route:
            DBStatistics.remove_fav(self.dbuser, route.dbroute)
            Activity.remove_favorite(self, route)
        return route

    def is_friend_of(self, friend):
        return self.key_name in friend.friends

    def connect_outsiteuser(self, outsiteuser):
        DBUser.connect_outsite_user(self.dbuser, outsiteuser.dboutuser)

    def post_activity(self, activity):
        for site in self.OutUsers.keys():
            self.OutUsers[site].post_activity(activity)

Anonymous = SiteUser.get_or_create("anonymous", "Anonymous")

class Comment(object):
    """ A Comment"""
    DBTYPE = DBComment

    def __init__(self, dbcomment=None):
        self.dbcomment = dbcomment

    def __getattr__(self, name):
        if name == "dbcomment":
            return None
        return getattr(self.dbcomment, name)

    def relocation(self, newlocation):
        self.dbcomment.location = newlocation
        self.dbcomment.save()
        return self

    @property
    def hasPhoto(self):
        return self.dbcomment.photourl and self.dbcomment.photourl != ""

    @property
    def Reviewer(self):
        return SiteUser(self.dbcomment.reviewer)

    @classmethod
    def get_by_id(cls, commentid):
        dbcomment = DBComment.get_by_id(commentid)
        return dbcomment and cls(dbcomment)

    @classmethod
    def get_comments_by_route_id(cls, routeid):
        from routes import Route
        route = Route.get_by_id(routeid)
        if route:
            return route.Comments
        return []

    @classmethod
    def create_comment(cls, reviewer, route, review, location, photourl="", category=CommentCategory.text):
        if category == CommentCategory.text and photourl:
            category = CommentCategory.photo
        dbcomment = DBComment.create(reviewer.dbuser, review, route=route.dbroute,
            location=location, photourl=photourl, category=category)
        if dbcomment:
            comment = cls(dbcomment=dbcomment)
            DBStatistics.add_comment(reviewer.dbuser, dbcomment)
            Activity.add_comment(reviewer, comment)
            return comment
        return None

    @classmethod
    def search_by_text(cls, search_text, pagesize=50, start_cursor=None, order="-posted_on"):
        if not is_orderable(cls.DBTYPE, order):
            order = '-posted_on'
        query = cls.DBTYPE.all().order(order).search(search_text, properties=cls.DBTYPE.SearchableProperties()[0])
        query.with_cursor(start_cursor=start_cursor)
        commentslist = [cls(dbcomment) for dbcomment in query.fetch(pagesize) if dbcomment is not None and not dbcomment.deleted]
        end_cursor = query.cursor()
        return commentslist,end_cursor

class Team(object):
    """ A Team"""
    DBTYPE = DBTeam


def main():
    pass

if __name__ == '__main__':
    main()
